﻿using System;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Components.Surface
{
    [Serializable]
    public class CanonicalGlassMaterial : SurfaceBsdf
    {

        protected RgbSpectrum Kr, Kt;
        protected MediumInfo Medium;
        private IFresnelTerm fresnel;

        public CanonicalGlassMaterial(RgbSpectrum r, RgbSpectrum s, MediumInfo m)
        {
            Kr = r;
            Kt = s;
            Medium = m;
            fresnel = new FresnelDielectric(1f, m.IoR);
        }

        public override void Sample_f(ref Vector lwo, ref Normal N, ref Normal shadeN, ref RgbSpectrum in_f, float u0, float u1, float u2, ref SurfaceTextureData surfaceData, out BsdfSampleData result)
        {
            result.Lambda = 0f;

            CreateFrame(ref shadeN);
            var wo = WorldToLocal(ref lwo);
             //((wiW & ng) * (woW & ng) > 0f)
            if (Vector.Dot(ref lwo, ref N)> 0f)
            {
                var wi = new Vector(-wo.x, -wo.y, wo.z);
                var f =  fresnel.Evaluate(CosTheta(ref wo)) * Kr /Math.Abs(CosTheta(ref wi));
                result.Wi = LocalToWorld(wi);
                result.Pdf = 1.0f;
                result.Type = BrdfType.Specular;
                result.F = f;
            }
            else
            {
                bool entering = CosTheta(ref wo) > 0f;
                result.Type = BrdfType.Refractive;
	            float ei = 1.0005f, et = Medium.IoR;
	            if (!entering)
		            MathLab.Swap(ref ei, ref et);
	            // Compute transmitted ray direction
	            float sini2 = SinTheta2(ref wo);
	            float eta = ei / et;
	            float sint2 = eta * eta * sini2;
	            // Handle total internal reflection for transmission
	            if (sint2 > 1f)
	            {
	                result.F = RgbSpectrum.ZeroSpectrum();
	                result.Pdf = 0f;
                    result.Wi = new Vector();
	            };
	            float cost = MathLab.Sqrt(Math.Max(MathLab.Epsilon, 1f - sint2));
                if (entering) cost = -cost;
	            float sintOverSini = eta;
	            var wi = new Vector(sintOverSini * -wo.x,
	                         sintOverSini * -wo.y,
				             cost);
	            result.Pdf = 1f;
	            var F = fresnel.Evaluate(CosTheta(ref wo));
                result.Wi = LocalToWorld(ref wi);
	            result.F = (ei*ei)/(et*et) * (RgbSpectrum.UnitSpectrum()-F) * Kt /Math.Abs(Math.Max(MathLab.Epsilon, CosTheta(ref wi)));
            }
        }

        public override bool IsDiffuse()
        {
            return false;
        }

        public override bool IsSpecular()
        {
            return true;
        }


        public override void f(ref Vector wo, ref Vector wi, ref Normal N, ref RgbSpectrum in_fs, out RgbSpectrum fs)
        {
            throw new NotImplementedException();
        }

        public override BrdfType Type
        {
            get { return BrdfType.Refractive | BrdfType.Specular; }
        }
    }
}